// awesometext.js
// Tarun Pondicherry
// Josh Israel

// This code is need of massive clean up.
// That's an understatement.

var UNICODE_RANGE = {
  NORMAL_UPPER: 0x41, 
  NORMAL_LOWER: 0x61,
  BOLD_UPPER: 0x1d400,
  BOLD_LOWER: 0x1d41a,
  ITALIC_UPPER: 0x1d434,
  ITALIC_LOWER: 0x1d44e,
  BOLDITALIC_UPPER: 0x1d468,
  BOLDITALIC_LOWER: 0x1d482,
  
  RANGE_SIZE: 26
};

/* Some character codes are in different ranges */
var UNICODE_INCONSISTENT_CHARS_R = {
  0x1d455: 0x210e // italics h
};
var UNICODE_INCONSISTENT_CHARS_F = {
  0x210e: 0x1d455 // italics h
};

var UNICODE_DIACRITICS = {
 UNDERLINE: 0x332,
 STRIKETHROUGH: 0x336
};

SURROGATE_HI = 0xd835;

// Taken from: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/fromCharCode
function fixedFromCharCode (codePt) {
    if (codePt > 0xFFFF) {
        codePt -= 0x10000;
        return String.fromCharCode(0xD800 + (codePt >> 10), 0xDC00 + (codePt & 0x3FF));
    }
    else {
        return String.fromCharCode(codePt);
    }
}
// End

function Character(attribChar) {
  this.baseCode = -1;
  this.originalChar = attribChar;
  this.unicodeRange = UNICODE_RANGE.NORMAL;
  this.isUnderline = false;
  this.isStrikethrough = false;
  
  var charCode;
  if (attribChar.charCodeAt(0) == SURROGATE_HI) {
    var lo = attribChar.charCodeAt(1);
    var hi = attribChar.charCodeAt(0);
    charCode = ((hi - 0xD800) * 0x400) + (lo - 0xDC00) + 0x10000;
  } else {
    charCode = attribChar.charCodeAt(0);
  }
  
  for (var strangeCode in UNICODE_INCONSISTENT_CHARS_F) {
    if (charCode == strangeCode) {
      charCode = UNICODE_INCONSISTENT_CHARS_F[strangeCode]; 
    }
  }
  
  for (var range in UNICODE_RANGE) {
    if (charCode >= UNICODE_RANGE[range] && charCode < (UNICODE_RANGE[range] + UNICODE_RANGE.RANGE_SIZE)) {
       this.unicodeRange = UNICODE_RANGE[range];
       this.baseCode = charCode - UNICODE_RANGE[range];
       break;
    }
  }

  // Diacritic Characters indicate underline or strikethrough
  this.isUnderline =
      ((attribChar.length > 1 && attribChar.charCodeAt(1) == UNICODE_DIACRITICS.UNDERLINE) ||
      (attribChar.length > 2 && attribChar.charCodeAt(2) == UNICODE_DIACRITICS.UNDERLINE)); 
  this.isStrikethrough =
      ((attribChar.length > 1 && attribChar.charCodeAt(1) == UNICODE_DIACRITICS.STRIKETHROUGH) ||
      (attribChar.length > 2 && attribChar.charCodeAt(2) == UNICODE_DIACRITICS.STRIKETHROUGH));
}

Character.prototype.toString = function() {
  if (this.baseCode == -1) {
    return this.originalChar; 
  } else {
    var string = '';
    var code = this.baseCode + this.unicodeRange;

    for (var strangeCode in UNICODE_INCONSISTENT_CHARS_R) {
      if (code == strangeCode) {
        code = UNICODE_INCONSISTENT_CHARS_R[strangeCode]; 
      }
    }
    
    string += fixedFromCharCode(code);
    
    if (this.isUnderline) {
      string += fixedFromCharCode(UNICODE_DIACRITICS.UNDERLINE); 
    }
    if (this.isStrikethrough) {
      string += fixedFromCharCode(UNICODE_DIACRITICS.STRIKETHROUGH); 
    }

    return string;
  }
}

/* Only pass *_LOWER ranges */
Character.prototype.setRange = function(range) {
  var isUpper = false;
  if ((this.unicodeRange == UNICODE_RANGE.NORMAL_UPPER) || 
      (this.unicodeRange == UNICODE_RANGE.BOLD_UPPER) ||
      (this.unicodeRange == UNICODE_RANGE.ITALIC_UPPER) ||
      (this.unicodeRange == UNICODE_RANGE.BOLDITALIC_UPPER)) {
    isUpper = true;
  }

  if (isUpper) {
    if (range == UNICODE_RANGE.NORMAL_LOWER) {
      this.unicodeRange = UNICODE_RANGE.NORMAL_UPPER;
    } else if (range == UNICODE_RANGE.BOLD_LOWER) {
      this.unicodeRange = UNICODE_RANGE.BOLD_UPPER;   
    } else if (range == UNICODE_RANGE.ITALIC_LOWER) {
      this.unicodeRange = UNICODE_RANGE.ITALIC_UPPER;
    } else if (range == UNICODE_RANGE.BOLDITALIC_LOWER) {
      this.unicodeRange = UNICODE_RANGE.BOLDITALIC_UPPER;
    }
  } else {
    this.unicodeRange = range;
  }
}

// Taken from: http://www.knowlegezone.com/documents/39/Javascript-Get-Selected-Text--Copy-Selected-Text/
function getSelectedText(txtarea)
{
  var txt = txtarea.value.substring(txtarea.selectionStart, txtarea.selectionEnd);
  return txt;
}
// End

function replaceSelectedText(txtarea, newtxt) {
  txtarea.value = txtarea.value.substring(0, txtarea.selectionStart) +
                  newtxt +
                  txtarea.value.substring(txtarea.selectionEnd);  
}

function textToChars(text) {
  var attribChar = '';
  var index = 0;
  var chars = Array();

  for (var i = 0; i < text.length; i++) {
    if (((text.charCodeAt(i) == UNICODE_DIACRITICS.UNDERLINE) ||
         (text.charCodeAt(i) == UNICODE_DIACRITICS.STRIKETHROUGH))) {
      attribChar += text[i];
    } else {
      if (attribChar != '') {
        chars[index++] = new Character(attribChar);
      }
      attribChar = text[i];
      if (text.charCodeAt(i) == SURROGATE_HI) {
        attribChar += text[++i];
      }
    }
  }
  chars[index++] = new Character(attribChar);

  return chars;
}

function charsToText(chars) {
  var string = '';

  for (var i = 0; i < chars.length; i++) {
    string += chars[i].toString();
  }

  return string;
}

function toggleProperty(obj, prop) {
  var ta = obj.parentNode.previousSibling;
  var text = getSelectedText(ta);
  var chars = textToChars(text);

  for (var i = 0; i < chars.length; i++) {
    chars[i].setRange(UNICODE_RANGE.NORMAL_LOWER);
    chars[i].isUnderline = false;
    chars[i].isStrikethrough = false;

    if (prop == 'b') {
      chars[i].setRange(UNICODE_RANGE.BOLD_LOWER);
    } else if (prop == 'i') {
      chars[i].setRange(UNICODE_RANGE.ITALIC_LOWER);
    } else if (prop == 'u') {
      chars[i].isUnderline = true;
    } else if (prop == 's') {
      chars[i].isStrikethrough = true;
    }
  }

  replaceSelectedText(ta, charsToText(chars));

  ta.focus();
}

function addToolbar(obj_textarea) {
  if (!(obj_textarea.nextSibling && obj_textarea.nextSibling.className == 'awesometext')) {
    var toolbar_div = document.createElement('div');
    toolbar_div.className = 'awesometext';
    toolbar_div.innerHTML = '<button type="button" id="st_b" onclick="toggleProperty(this, \'b\'); return false;"><b>B</b></button>' +
                            '<button type="button" id="st_i" onclick="toggleProperty(this, \'i\'); return false;"><i>I</i></button>' +
                            '<button type="button" id="st_u" onclick="toggleProperty(this, \'u\'); return false;"><u>U</u></button>' +
                            '<button type="button" id="st_s" onclick="toggleProperty(this, \'s\'); return false;"><strike>S</strike></button>' +
                            '<button type="button" id="st_c" onclick="toggleProperty(this, \'c\'); return false;">Clear</button>' +
                            '<br />' +
                            '<span style="font-size: 8pt;">Bold and Italics do not render on Chrome, Safari, and Android Browser < 3.0.' + 
                            'We hope that Unicode support will improve in future releases of these environments.</span>';
    obj_textarea.parentNode.insertBefore(toolbar_div, obj_textarea.nextSibling);
  }
}

function addToolbars() {
  var textareas = document.getElementsByTagName('textarea');
  
  for (var i = 0; i < textareas.length; i++) {
    addToolbar(textareas[i]);
  }
}
